import {Injectable} from '@angular/core';
import {Observable} from 'rxjs/index';
import {TpHttpService} from './tp-http.service';
import {Doc} from '../entity/doc';
import {PageVo} from '../entity/page-vo';
import {Chapter} from '../entity/chapter';
import {Section} from '../entity/section';
import {DocApi} from '../entity/doc-api';
import {TpStorageService} from './tp-storage.service';
import {tap} from 'rxjs/operators';
import {LinkDto} from '../entity/link-dto';
import {Entity} from '../entity/entity';
import {PageVoService} from './page-vo.service';
import {Router} from '@angular/router';
import {UserService} from './user.service';
import {Env} from '../entity/env';
import {MenuService} from './menu.service';
import {MenuRoutes} from './menu-routes';
import {SimpleUser} from '../entity/simple-user';

@Injectable({
    providedIn: 'root'
})
export class DocService {
    /**
     * 接口ID与接口的映射
     * apiId:string => api:DocApi
     */
    private apiIdMap = new Map<string, DocApi>();

    /**
     * 实体类名称与实体类的映射
     * @type {Map<string, Entity>}
     */
    private entityMap: Map<string, Entity>;

    /**
     * 当前所在文档
     */
    private currentDoc: Doc;

    /**
     * 当前选中api
     */
    private currentApi: DocApi;

    /**
     * 当前文档的所有者
     */
    private owners: SimpleUser[];

    /**
     * 最新的版本号，有可能跟当前版本号不一致
     */
    private lastDocVersion = 0;

    constructor(private httpService: TpHttpService,
                private tpStorageService: TpStorageService,
                private pageVoService: PageVoService,
                private menuService: MenuService,
                private userService: UserService,
                private router: Router) {

        // 启动版本号检查(1分钟检查一次)
        setInterval(() => {
            this.checkNewDoc();
        }, 60000);
    }

    public getCurrentDoc(): Doc {
        return this.currentDoc;
    }

    public getDocVersion(docName: string): Observable<string> {
        return this.httpService.get(`/api/doc/${docName}/version`);
    }

    /**
     * 获取文档
     * @param {string} docId 文档ID
     * @param {number | string} docVersion 指定的版本号
     * @returns {Observable<Doc>}
     */
    public getDoc(docId: string, docVersion?: number | string): Observable<Doc> {
        let key = TpStorageService.KEY_DOC + docId;
        if (docVersion) {
            key += '-' + docVersion;
        }
        const doc: Doc = this.tpStorageService.getObj(key);
        if (doc) {
            this.initEntities(doc.entities);
            this.currentDoc = doc;
            this.loadDocOwners();
            this.checkNewDoc();
            return new Observable<Doc>(observer => observer.next(doc));
        }
        let url = `/api/doc/${docId}/`;
        if (docVersion) {
            url += '?version=' + docVersion;
        }
        return this.httpService.get(url)
            .pipe(
                tap((data: Doc) => {
                        data.updateTime = new Date().getTime();
                        this.tpStorageService.set(key, JSON.stringify(data));

                        // 初始化entityMap
                        this.initEntities(data.entities);
                        this.currentDoc = data;
                        this.loadDocOwners();
                    }, e => {
                        console.log(e);
                    }, () => {
                    }
                )
            );
    }

    public getBaseDocPageVo(leftMenuId: string, menuRoutes: MenuRoutes): PageVo {
        const pageVo: PageVo = this.pageVoService.getPageVo();

        if (pageVo.leftMenuId !== leftMenuId) {
            // 需要调整
            pageVo.leftMenuId = leftMenuId;
            pageVo.leftMenus = this.menuService.getDocLeftMenus(this.currentDoc, menuRoutes, this.userService.getUser());
        }

        const title = this.currentDoc.artifact.artifactId;
        const routerLink = ['/doc', this.getCurrentDocId()];

        pageVo.title = title;
        pageVo.crumbs = <LinkDto[]>[
            {
                text: '项目索引',
                routerLink: [''],
                frontIcon: 'home'
            }, {
                text: title,
                routerLink: routerLink,
                frontIcon: 'view_list'
            }
        ];

        return pageVo;
    }

    public getEnv(foceFetch): Observable<Env> {
        const currentDocId = this.getCurrentDocId();
        const key = TpStorageService.KEY_ENV + currentDocId;
        if (!foceFetch) {
            const existsEnv: Env = this.tpStorageService.getObj(key);
            if (existsEnv) {
                return new Observable<Env>(observer => observer.next(existsEnv));
            }
        }

        return this.httpService.get('/api/env/' + currentDocId + '/').pipe(
            tap((env: Env) => {
                    this.tpStorageService.set(key, JSON.stringify(env));
                }, e => {
                    console.log(e);
                },
                () => {
                }
            )
        );
    }

    public getApi(apiId: string): DocApi {
        this.currentApi = this.apiIdMap.get(apiId);
        if (this.currentDoc.chapters == null || this.currentApi) {
            return this.currentApi;
        }
        this.currentDoc.chapters.forEach((chapter: Chapter) => {
            if (chapter.sections) {
                chapter.sections.forEach((section: Section) => {
                    if (section.apis) {
                        section.apis.forEach((api: DocApi) => {
                            this.apiIdMap.set(api.key, api);
                            if (api.key === apiId) {
                                this.currentApi = api;
                            }
                        });
                    }
                });
            }
        });
        return this.currentApi;
    }

    public getEntity(entityName: string): Entity {
        return this.entityMap.get(entityName);
    }

    isCurrentDocOwner() {
        const docId = this.getCurrentDocId();
        return this.userService.isDocOwner(docId);
    }

    private initEntities(entities: Entity[]) {
        this.entityMap = new Map<string, Entity>();
        if (!entities) {
            return;
        }
        entities.forEach((entity: Entity) => {
            this.entityMap.set(entity.name, entity);
        });
    }

    private checkNewDoc() {
        const currentDoc: Doc = this.currentDoc;
        if (!currentDoc) {
            return;
        }
        const currentDocId = currentDoc.artifact.id;
        const currentDocVersion = currentDoc.artifact.docVersion;
        this.httpService.get(`/api/doc/fetch/${currentDocId}/?version=${currentDocVersion}`)
            .subscribe((data: Doc) => {
                if (data) {
                    // 更新本地存储
                    const docKey = TpStorageService.KEY_DOC + currentDoc.artifact.id;
                    this.tpStorageService.set(docKey + '-' + currentDoc.artifact.docVersion, JSON.stringify(currentDoc));
                    this.tpStorageService.set(docKey, JSON.stringify(data));

                    const links = ['/doc', currentDoc.artifact.id];
                    if (this.currentApi) {
                        links.push(this.currentApi.key);
                    }
                    links.push('v', currentDoc.artifact.docVersion.toString());
                    // 跳转到新的URL
                    this.router.navigate(links);
                }
            });
    }

    /**
     * 获取当前文档ID
     * @returns {string}
     */
    private getCurrentDocId() {
        return `${this.currentDoc.artifact.groupId}:${this.currentDoc.artifact.artifactId}`;
    }

    private loadDocOwners() {
        this.httpService.get(`/api/doc/${this.getCurrentDocId()}/owner`).subscribe((owners: SimpleUser[]) => {
            this.owners = owners;
            this.pageVoService.ownerBehavior.next(this.owners);
        });
    }
}
